﻿using System;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using TiffLibrary;
using TiffLibrary.PixelBuffer;
using TiffLibrary.PixelFormats;
using WholeSlideImageLibrary;

namespace WholeSlideImageViewer.TiffExport
{
    internal sealed class TileRegionReader : ITiffPixelBufferReader<TiffRgba32>
    {
        private readonly WholeSlideImage _image;
        private readonly TiffExportProgress _progress;
        private readonly int _width;
        private readonly int _height;

        public int Width => _width;
        public int Height => _height;

        public TileRegionReader(WholeSlideImage image, TiffExportProgress progress)
        {
            _image = image;
            _progress = progress;
            _width = image.Width;
            _height = image.Height;
        }

        public async ValueTask ReadAsync(TiffPoint offset, TiffPixelBufferWriter<TiffRgba32> destination, CancellationToken cancellationToken)
        {
            byte[] buffer = ArrayPool<byte>.Shared.Rent(destination.Width * destination.Height * 4);
            try
            {
                await _image.ReadRegionAsync(0, offset.X, offset.Y, destination.Width, destination.Height, buffer.AsMemory(0, destination.Width * destination.Height * 4), cancellationToken).ConfigureAwait(false);

                Copy(buffer, destination);
            }
            finally
            {
                ArrayPool<byte>.Shared.Return(buffer);
            }

            _progress.NotifyOneTileWritten();
        }

        private static void Copy(ReadOnlySpan<byte> source, TiffPixelBufferWriter<TiffRgba32> destination)
        {
            int height = destination.Height;
            for (int i = 0; i < height; i++)
            {
                using TiffPixelSpanHandle<TiffRgba32> destinationHandle = destination.GetRowSpan(i);
                ReadOnlySpan<byte> sourceSpan = source.Slice(destination.Width * i * 4, destination.Width * 4);
                sourceSpan.CopyTo(MemoryMarshal.AsBytes(destinationHandle.GetSpan()));
            }
        }
    }
}
